---
title: Products
---

## Overview

`Product` records track unique products within your store. These differ from [Variants](products#variants), which track the unique variations of a product. For instance, a product that's a T-shirt would have variants denoting its different colors and sizes. Together, Products and Variants describe what is for sale. 

<Info>
Note that as of version 4.6, certain product fields are translatable (read more about this in [Internationalization](../customization/i18n#resource-translations)).
</Info>

Products have the following attributes:

| Attribute         | Description                                                                                           | Translatable |
|-------------------|-------------------------------------------------------------------------------------------------------|--------------|
| `name`            | Short name for the product                                                                            | Yes          |
| `description`     | The most elegant, poetic turn of phrase for describing your product's benefits and features          | Yes          |
| `slug`            | SEO slug based on the product name that is placed into the URL for the product. We use a library called [friendly_id](https://github.com/norman/friendly_id) to generate them                     | Yes          |
| `status`          | The status of the product. Can be `draft`, `active`, `archived`. Defaults to `draft`. | No           |
| `available_on`    | The first date the product becomes available for sale online in your shop                            | No           |
| `discontinue_on`  | Date when the product will become unavailable for sale online in your shop                            | No           |
| `deleted_at`      | The date the product is marked as deleted. We don't remove products entirely, only soft deleting them using a library called                                                              | No           |
| `meta_title`      | Optional title used for search engines instead of `name`                                              | Yes          |
| `meta_description`| A description targeted at search engines for search engine optimization (SEO)                         | Yes          |
| `meta_keywords`   | Several words and short phrases separated by commas, also targeted at search engines                  | Yes          |

To understand how variants come to be, you must first understand option types and option values.

## Option Types and Option Values

Option types denote the different options for a variant.

A typical option type would be a `size`, with that option type's values being something such as `Small`, `Medium` and `Large`. 

Another typical option type could be a `color`, such as `Red`, `Green`, or `Blue`.

A product can be assigned many option types, but must be assigned at least one if you wish to create variants for that product

<Info>
The `name` and `presentation` fields for option types are translatable as of version 4.6.
</Info>

## Variants

`Variant` records track the individual variants of a `Product`. Variants are of two types: master variants and normal variants.

Variant records can track some individual properties regarding a variant, such as height, width, depth, and cost price. These properties are unique to each variant, and so are different from [Product Properties](products#product-properties), which apply to all variants of that product.

| Attribute          | Description                                                                                   | Example Value          |
|--------------------|-----------------------------------------------------------------------------------------------|------------------------|
| `sku`              | Unique identifier for each variant                                                            | `123TSHRT-M-G`      |
| `barcode`              | Barcode code                                                           | ``      |
| `barcode`              | A unique code that represents a variant, often used for scanning purposes. | `123456789` |
| `weight`           | The weight of the variant                                                                      | 2               |
| `height`           | The height of the variant                                                                     | 150               |
| `width`            | The width of the variant                                                                      | 150                |
| `depth`            | The depth of the variant                                                                      | 100                 |
| `is_master`        | Indicates if the variant is a master variant                                                  | `false`                  |
| `track_inventory`  | Indicates if the inventory is tracked for this variant                                        | `true`                   |
| `cost_price`       | The cost price of the variant                                                                 | 5.00                   |
| `cost_currency`    | The currency of the cost price                                                                | `USD`                  |
| `discontinue_on`   | Date when the variant will become unavailable for sale                                        | 2023-12-31 18:00           |

### Master Variants

Every single product has a master variant, which tracks basic information such as a count on hand, a price and a <Tooltip tip="Stock Keeping Unit - In inventory management, a stock keeping unit (abbreviated as SKU) is the unit of measure in which the stocks of a material are managed.">SKU</Tooltip>.

Whenever a product is created, a master variant for that product will be created too.

<Info>
Logic behind is implemented in the `after_initialize` callback of the `Spree::Product` model in a [ensure_master](https://github.com/spree/spree/blob/d3927a3f646f0b4e72b137bf72606c885ec59829/core/app/models/spree/product.rb#L132) method.
</Info>

Master variants are automatically created along with a product and exist for the sole purpose of having a consistent API when associating variants and [line items](orders#line-items). If there were no master variant, then line items would need to track a polymorphic association which would either be a product or a variant.

### Regular Variants

Variants which are not the master variant are unique based on a [option type and option value](products#option-types-and-option-values) combinations.

For instance, you may be selling a product which is a Baseball Jersey, which comes in the sizes "Small", "Medium" and "Large", as well as in the colors of "Red", "Green". For this combination of sizes and colors, you would be able to create 9 unique variants:

| SKU       | Size  | Color |
|-----------|-------|-------|
| SKU-S-R   | Small | Red   |
| SKU-S-G   | Small | Green |
| SKU-M-R   | Medium| Red   |
| SKU-M-G   | Medium| Green |
| SKU-L-R   | Large | Red   |
| SKU-L-G   | Large | Green |

### Default Variant

This all can sound complex and confusing, so we'll simplify things for you.

To get the default Variant for a product, you can call:

```ruby
product.default_variant
```

How this works?

* If a product has multiple Variants it will return the first non-master Variant based on their sort position set in the Admin Panel or Platform API.
* If there are no non-master Variants it will return the Master Variant

So you can easily rely on this method to get the default Variant for a product.

## Images

Images can be associated to the  Product (via master variant) or to the individual Variants.

Product images can be fetched via:

```ruby
product.images
```

To get all images for product and all it's variants, call `variant_images` on the product:

```ruby
product.variant_images
```

To fetch individual variant images, call `images` on the variant:

```ruby
product.default_variant.images
```

Image order is determined by the `position` attribute, which is an integer. By default, images are ordered from left to right.

<Info>
Under the hood we use [Rails Active Storage](https://guides.rubyonrails.org/active_storage_overview.html) to handle image storage and manipulation.

More information about images can be found in the [Images Customization](../customization/images) section.
</Info>

## Product Properties

Product properties track individual attributes for a product that don't apply to all products. These are typically additional information about the item. For instance, a T-Shirt may have properties representing information about the kind of material used, as well as the type of fit the shirt is.

A `Property` should not be confused with an [`OptionType`](products#option-types-and-option-values), which is used when defining [Variants](products#variants) for a product.

You can retrieve the value for a property on a `Product` object by calling the `property` method on it and passing through that property's name:

```ruby
product.property("material")
=> "100% Cotton"
```

You can set a property on a product by calling the `set_property` method:

```ruby
product.set_property("material", "100% cotton")
```

If this property doesn't already exist, a new `Property` instance with this name will be created on the fly.

<Info>
As of version 4.6, product property `value` fields are translatable.
</Info>

## Prices

`Price` objects track a price for a particular currency and variant combination. For instance, a [Variant](products#variants) may be available for $15 (`15 USD`) and €7 (`7 Euro`).

`Price` object contains 3 important attributes:

| Attribute         | Description                                                                                   | Example Value         |
|-------------------|-----------------------------------------------------------------------------------------------|-----------------------|
| `amount`          | The current selling price of the variant in the specified currency.                           | `99.90`             |
| `compare_at_amount` | The recommended retail price of the variant in the specified currency. This can be used to display crossed out prices in the storefront. | `129.90`             |
| `currency`        | The ISO code for the currency in which the `amount` and `compare_at_amount` are denominated. | `USD`                 |

<Warning>
If a product doesn't have a price in the selected currency it won't show up in the Storefront and Storefront API by default.
</Warning>

To fetch a list of currencies that given product is available in, call `prices` to get a list of related `Price` objects:

```ruby
product.prices
=> [#<Spree::Price id: 2 ...]
```

To find a list of currencies that Variant is available in, call `prices` to get a list of related `Price` objects:

```ruby
product.default_variant.prices
=> [#<Spree::Price id: 2 ...]
```

To find Product price in a selected currency via [ISO symbol](https://www.iban.com/currency-codes):

```ruby
product.price_in('EUR')
=> #<Spree::Price id: 232, variant_id: 232, amount: 0.8499e2, currency: "EUR", deleted_at: nil, created_at: "2021-08-16 19:41:55.888522000 +0000", updated_at: "2021-08-16 19:41:55.888522000 +0000", compare_at_amount: nil, preferences: nil>
```

<Note>
If there's no price set for requested currency this will return a new Price object with the currency set to the requested currency. It will not be a persisted object.
</Note>

To find Variant's price in a selected currency:

```ruby
product.default_variant.price_in('EUR')
=> #<Spree::Price id: 232, variant_id: 232, amount: 0.8499e2, currency: "EUR", deleted_at: nil, created_at: "2021-08-16 19:41:55.888522000 +0000", updated_at: "2021-08-16 19:41:55.888522000 +0000", compare_at_amount: nil, preferences: nil>
```

There are also other helpful methods available such as:

<AccordionGroup>
  <Accordion title="Getting amount (number)">
  
  ```ruby
  product.default_variant.amount_in('EUR')
   => 0.8499e2
  ```
  
  </Accordion>
  <Accordion title="Getting amount (string)">
  
  ```ruby
  product.default_variant.amount_in('EUR').to_s
   => "84.99"
  ```
  
  </Accordion>
  <Accordion title="Displaying price with currency symbol">
  
  ```ruby
  product.price_in('EUR').display_amount.to_s
   => "€84.99"
  ```
  
  </Accordion>
</AccordionGroup>

<Info>
Spree behind the scenes uses [Ruby Money gem](https://github.com/RubyMoney/money) with some [additional](https://github.com/spree/spree/blob/main/core/app/models/concerns/spree/display_money.rb) [tweaks](https://github.com/spree/spree/blob/main/core/lib/spree/money.rb).
</Info>

## Prototypes

A prototype is a useful way to share common `OptionType` and `Property` combinations amongst many different products. They work as a product template to speed up the creation of new products.

For instance, if you're creating a lot of clothing products, you may wish to maintain the "Size" and "Color" option types, as well as a "Fitting Type" property.

## Taxons and Taxonomies

Taxonomies provide a simple, yet robust way of categorizing products by enabling store administrators to define as many separate structures as needed.

When working with Taxonomies there are two key terms to understand:

* `Taxonomy` – a hierarchical list which is made up of individual Taxons. Each taxonomy relates to one `Taxon`, which is its root node.
* `Taxon` – a single child node which exists at a given point within a `Taxonomy`. Each `Taxon` can contain many (or no) sub / child taxons. Store administrators can define as many Taxonomies as required, and link a product to multiple Taxons from each Taxonomy.

By default, both Taxons and Taxonomies are ordered by their `position` attribute.

<Note>
Taxons use the [Nested set model](http://en.wikipedia.org/wiki/Nested_set_model) for their hierarchy. The `lft` and `rgt` columns in the `spree_taxons` table represent the locations within the hierarchy of the item. This logic is handled by the [awesome nested set](https://github.com/collectiveidea/awesome_nested_set) gem.
</Note>

Taxons link to products through an intermediary model called `Classification`. This model exists so that when a product is deleted, all of the links from that product to its taxons are deleted automatically. A similar action takes place when a taxon is deleted; all of the links to products are deleted automatically.

<Note>
Storefront uses `spree.nested_taxons_path` helper method to generate full taxon URLs, which will use the taxon's permalink, eg. 

```ruby
taxon = Spree::Taxon.find_by(permalink: 'clothes/dresses')
spree.nested_taxons_path(taxon)
```

which will output `/t/categories/clothes/dresses`.
</Note>

<Info>
As of version 4.6, the taxon `name` and `description` fields are translatable.
</Info>
